/**
 * 
 */
package com.google.code.jkippt;

import java.util.Iterator;
import java.util.Map;

import com.google.code.jkippt.http.APIClient;
import com.google.code.jkippt.http.HttpAPIClient;
import com.google.code.jkippt.http.ResourceEndpointUrlFactory;
import com.google.code.jkippt.http.paging.PageIterator;
import com.google.code.jkippt.http.paging.PagedResultItemIterator;
import com.google.code.jkippt.parser.PageParserBuilder;
import com.google.code.jkippt.parser.Parser;
import com.google.code.jkippt.resource.Resource;

/**
 * A {@link KipptEndpoint} for a generic {@link Resource}.
 * This class offers a set of common methods.
 * @author "Federico De Faveri defaveri@gmail.com"
 * @param <T> the specific {@link Resource} type.
 */
public class ResourceEndpoint<T extends Resource> {
	
	protected APIClient client;
	protected Parser parser;
	protected ResourceEndpointUrlFactory endpointUrlFactory;
	
	protected Class<T> resourceClass;
	
	/**
	 * Creates new {@link KipptEndpoint} for generic {@link Resource}.
	 * @param client the {@link HttpAPIClient} to use.
	 * @param parser the {@link Parser} to use.
	 * @param endpointUrlFactory the enpoint factory to use.
	 * @param resourceClass the {@link Resource} {@link Class}.
	 */
	public ResourceEndpoint(APIClient client, Parser parser, ResourceEndpointUrlFactory endpointUrlFactory, Class<T> resourceClass) {
		this.client = client;
		this.parser = parser;
		this.endpointUrlFactory = endpointUrlFactory;
		this.resourceClass = resourceClass;
	}
	
	/**
	 * Adds a new {@link Resource}. The {@link Resource} have to be new.
	 * @param resource the {@link Resource} to add.
	 * @return the added and updated {@link Resource}.
	 * @throws JKipptException
	 */
	public T add(T resource) throws JKipptException
	{
		if (resource.getId()!=null) throw new IllegalArgumentException("The passed resource has already an id, you can only update it");
		String json = parser.toJson(resource);
		String endpoint = endpointUrlFactory.buildEndpointUrl();
		String newJson = client.post(endpoint, json);
		return parser.parse(newJson, resourceClass);
	}
	
	/**
	 * Updates the specified {@link Resource}.
	 * @param resource the {@link Resource} to update.
	 * @return the updated {@link Resource}.
	 * @throws JKipptException
	 */
	public T update(T resource) throws JKipptException
	{
		String json = parser.toJson(resource);
		String endpoint = endpointUrlFactory.buildEndpointUrl(resource.getId());
		String newJson = client.put(endpoint, json);
		return parser.parse(newJson, resourceClass);
	}
	
	/**
	 * Gets the specified {@link Resource}.
	 * @param id the {@link Resource} id.
	 * @return the retrieved {@link Resource}.
	 * @throws JKipptException if an error occurs.
	 */
	public T get(String id) throws JKipptException
	{
		String endpoint = endpointUrlFactory.buildEndpointUrl(id);
		String json = client.get(endpoint);
		return parser.parse(json, resourceClass);
	}
	
	/**
	 * Gets the specified {@link Resource}.
	 * @param endpoint the {@link Resource} endpoint.
	 * @return the retrieved {@link Resource}.
	 * @throws JKipptException if an error occurs.
	 */
	public T getFromEndpoint(String endpoint) throws JKipptException
	{
		String json = client.get(endpoint);
		return parser.parse(json, resourceClass);
	}
	
	/**
	 * Deletes the specified {@link Resource}.
	 * @param id the {@link Resource} id.
	 * @throws JKipptException if an error occurs.
	 */
	public void delete(String id) throws JKipptException
	{
		String endpoint = endpointUrlFactory.buildEndpointUrl(id);
		client.delete(endpoint);
	}
	
	/**
	 * Gets all the available {@link Resource}s.
	 * @return the retrieved {@link Resource}s.
	 * @throws JKipptException if an error occurs.
	 */
	public Iterator<T> getAll() throws JKipptException
	{
		String firstPageEndPoint = endpointUrlFactory.buildEndpointUrl();
		return getPaginated(firstPageEndPoint);
	}
	
	/**
	 * Queries for the {@link Resource}s using the specified parameters.
	 * @param parameters the query parameters.
	 * @return the result stream.
	 * @throws JKipptException
	 */
	public Iterator<T> query(Map<String, String> parameters) throws JKipptException
	{
		String firstPageEndPoint = endpointUrlFactory.buildEndpointUrl(parameters);
		return getPaginated(firstPageEndPoint);
	}
	
	/**
	 * Queries for the {@link Resource}s using the specified parameters.
	 * @param parameters the query parameters.
	 * @return the result stream.
	 * @throws JKipptException
	 */
	public Iterator<T> query(String subpath, Map<String, String> parameters) throws JKipptException
	{
		String firstPageEndPoint = endpointUrlFactory.buildEndpointUrl(subpath, parameters);
		return getPaginated(firstPageEndPoint);
	}
	
	/**
	 * Get a stream of paginated {@link Resource}s from the specified endpoint.
	 * @param firstPageEndpoint the first page endpoint url.
	 * @return the {@link Resource} stream.
	 * @throws JKipptException if an error occurs retrieving the stream.
	 */
	public Iterator<T> getPaginated(String firstPageEndpoint) throws JKipptException
	{
		PageParserBuilder<T> parserBuilder = parser.getPageParserBuilder(resourceClass);
		PageIterator<T> pager = new PageIterator<T>(client, parserBuilder, firstPageEndpoint);
		return new PagedResultItemIterator<T>(pager);
	}

}
